/**
 *   Copyright (C) Sangfor Corporation. All rights reserved.

 *   \file     ip_address.h
 *   \author   RainVan(Yunfeng.Xiao)
 *   \date     Jun 2011
 *   \version  1.0.0
 *   \brief    Interface of IP address function
 */

#ifndef __IP_ADDRESS_H__
#define __IP_ADDRESS_H__

#ifdef __KERNEL__
#   include <linux/types.h>
#   include <linux/socket.h>
#   include <linux/in.h>
#   include <linux/in6.h>
#   include <linux/inet.h>
#   include <linux/string.h>
#else
#   include <sys/types.h>
#   include <sys/socket.h>
#   include <netinet/in.h>
#   include <arpa/inet.h>
#   include <string.h>
#endif /*__KERNEL__*/

/*! define, then initialize an IP address
 */
#define IP_ADDRESS_DEFINE(val)  ip_address_t (val)={0};

#if defined(__cplusplus)
extern "C" {
#endif

    /*! IP address
     */
    typedef struct ip_address_t
    {
        /* family of address */
        uint16_t  family;

        /* user-parameter of address */
        uint16_t  param;

        /* data of address */
        union 
        {
            uint32_t  all[4];
            uint32_t  ip;
            uint32_t  ip6[4];
            struct in_addr  in;
            struct in6_addr in6;
        };

    } ip_address_t;

    /**
     *  \brief          Retrieve size of ip address
     *  \return         The value of size
     */
    __inline static
    int ip_address_size(void)
    {
        return (int)sizeof(ip_address_t);
    }

    /**
     *  \brief          Retrieve family of ip address
     *  \param addr     The pointer of ip address
     *  \return         The value of family (0, AF_INET, AF_INET6)
     */
    __inline static
    uint16_t ip_address_family(const ip_address_t *addr)
    {
        return (addr->family);
    }

    /**
     *  \brief          Retrieve non-zero if it is an ipv4 address
     *  \param addr     The pointer of ip address
     *  \return         1 indicates true, 0 indicates false
     */
    __inline static
    int ip_address_is_ipv4(const ip_address_t *addr)
    {
        return (addr->family == AF_INET);
    }

    /**
     *  \brief          Retrieve non-zero if it is an ipv6 address
     *  \param addr     The pointer of ip address
     *  \return         1 indicates true, 0 indicates false
     */
    __inline static
    int ip_address_is_ipv6(const ip_address_t *addr)
    {
        return (addr->family == AF_INET6);
    }

    /**
     *  \brief          Retrieve non-zero if it is a valid address
     *  \param addr     The pointer of ip address
     *  \return         1 indicates true, 0 indicates false
     */
    __inline static
    int ip_address_is_valid(const ip_address_t *addr)
    {
        return (addr->family == AF_INET || addr->family == AF_INET6);
    }

    /**
     *  \brief          Retrieve parameter of ip address
     *  \param addr     The pointer of ip address
     *  \return         The value of parameter
     */
    __inline static
    uint16_t ip_address_get_param(const ip_address_t *addr)
    {
        return (addr->param);
    }

    /**
     *  \brief          Retrieve ip of an ipv4 address
     *  \param addr     The pointer of ip address
     *  \return         The value of ip
     */
    __inline static
    uint32_t ip_address_get_ip(const ip_address_t *addr)
    {
        return (addr->ip);
    }

    /**
     *  \brief          Retrieve ip of an ipv6 address
     *  \param addr     The pointer of ip address
     *  \return         The pointer of ip-array
     */
    __inline static
    const uint32_t *ip_address_get_ip6(const ip_address_t *addr)
    {
        return (addr->ip6);
    }

    /**
     *  \brief          Retrieve in_addr of an ipv4 address
     *  \param addr     The pointer of ip address
     *  \return         The pointer of in_addr
     */
    __inline static
    const struct in_addr *ip_address_get_in(const ip_address_t *addr)
    {
        return &(addr->in);
    }

    /**
     *  \brief          Retrieve in6_addr of an ipv6 address
     *  \param addr     The pointer of ip address
     *  \return         The pointer of in6_addr
     */
    __inline static
    const struct in6_addr *ip_address_get_in6(const ip_address_t *addr)
    {
        return &(addr->in6);
    }

    /**
     *  \brief          Set parameter of ip address
     *  \param addr     The pointer of ip address
     *  \param param    The value of parameter
     */
    __inline static
    void ip_address_set_param(ip_address_t *addr, uint16_t param)
    {
        addr->param = param;
    }

    /**
     *  \brief          Set an ipv4 address
     *  \param addr     The pointer of ip address
     *  \param ip       The value of ip
     */
    __inline static
    void ip_address_set_ip(ip_address_t *addr, uint32_t ip)
    {
        addr->family = AF_INET;
        addr->ip = ip;
    }

    /**
     *  \brief          Set an ipv6 address
     *  \param addr     The pointer of ip address
     *  \param ip6      The pointer of ip-array
     */
    __inline static
    void ip_address_set_ip6(ip_address_t *addr, const uint32_t ip6[4])
    {
        addr->family = AF_INET6;
        memcpy(&addr->ip6, ip6, sizeof(addr->ip6));
    }

    /**
     *  \brief          Set an ipv4 address
     *  \param addr     The pointer of ip address
     *  \param in       The pointer of in_addr
     */
    __inline static
    void ip_address_set_in(ip_address_t *addr, const struct in_addr *in)
    {
        addr->family = AF_INET;
        memcpy(&addr->in, in, sizeof(addr->in));
    }

    /**
     *  \brief          Set an ipv6 address
     *  \param addr     The pointer of ip address
     *  \param in6      The pointer of in6_addr
     */
    __inline static
    void ip_address_set_in6(ip_address_t *addr, const struct in6_addr *in6)
    {
        addr->family = AF_INET6;
        memcpy(&addr->in6, in6, sizeof(addr->in6));
    }

    /**
     *  \brief          Convert an IPv4 string format address to network format
     *  \param src      The string of source
     *  \param dst      The buffer for receiving result
     *  \return         0 indicates success, -1 indicates failure.
     */
    __inline static
    int ip_address_pton4(const char *src, void *dst)
    {
        uint8_t tmp[4] = { 0 };
        uint8_t *tp = tmp;

        int octets = 0;
        int saw_digit = 0;
        int ch = 0;

        /* scanning string */
        while ((ch = *src++) != '\0')
        {
            if (ch >= '0' && ch <= '9')
            {
                int val = (*tp) * 10 + (ch - '0');

                if (saw_digit && *tp == 0)
                {
                    return -1;
                }
                if (val > 255)
                {
                    return -1;
                }
                *tp = (uint8_t)val;
                if (!saw_digit)
                {
                    if (++octets > 4)
                    {
                        return -1;
                    }
                    saw_digit = 1;
                }
            } 
            else if (ch == '.' && saw_digit) 
            {
                if (octets == 4)
                {
                    return -1;
                }
                *(++tp) = 0;
                saw_digit = 0;
            }
            else
            {
                return -1;
            }
        }
        if (octets < 4)
        {
            return -1;
        }
        memcpy(dst, tmp, sizeof(tmp));
        return 0;
    }

    __inline static
    int ip_address_chtod(int ch)
    {
        switch (ch)
        {
        case '0': return 0;
        case '1': return 1;
        case '2': return 2;
        case '3': return 3;
        case '4': return 4;
        case '5': return 5;
        case '6': return 6;
        case '7': return 7;
        case '8': return 8;
        case '9': return 9;
        case 'a': case 'A': return 10;
        case 'b': case 'B': return 11;
        case 'c': case 'C': return 12;
        case 'd': case 'D': return 13;
        case 'e': case 'E': return 14;
        case 'f': case 'F': return 15;
        }
        return -1;
    }

    /**
     *  \brief          Convert an IPv6 string format address to network format
     *  \param src      The string of source
     *  \param dst      The buffer for receiving result
     *  \return         0 indicates success, -1 indicates failure.
     */
    __inline static
    int ip_address_pton6(const char *src, void *dst)
    {
        uint8_t tmp[16] = { 0 };
        uint8_t *tp = tmp;
        uint8_t *end = tp + sizeof(tmp);
        uint8_t *colon = 0;

        const char *curtok = src;
        int seen_xdigits = 0;
        int ch = 0;
        int val = 0;

        /* Leading :: requires some special handling. */
        if (*src == ':')
        {
            if (*++src != ':')
            {
                return -1;
            }
        }

        while ((ch = *src++) != '\0') 
        {
            int hex = ip_address_chtod(ch);

            if (hex != -1)
            {
                val <<= 4;
                val |= hex;
                if (++seen_xdigits > 4)
                {
                    return -1;
                }
                continue;
            }
            if (ch == ':') 
            {
                curtok = src;
                if (!seen_xdigits) 
                {
                    if (colon)
                    {
                        return -1;
                    }
                    colon = tp;
                    continue;
                }
                else if (*src == '\0') 
                {
                    return -1;
                }
                /* enough space? */
                if (tp + 2 > end)
                {
                    return -1;
                }
                *tp++ = (uint8_t)(val >> 8) & 0xff;
                *tp++ = (uint8_t)val & 0xff;
                seen_xdigits = 0;
                val = 0;
                continue;
            }
            /* IPv4 part? e.g. x:x:x:x:x:x:d.d.d.d */
            if (ch == '.' && ((tp + 4) <= end) &&
                ip_address_pton4(curtok, tp) == 0)
            {
                tp += 4;
                seen_xdigits = 0;
                break;  /* '\0' was seen by ip_address_pton4(). */
            }
            return -1;
        }
        if (seen_xdigits) 
        {
            /* enough space? */
            if (tp + 2 > end)
            {
                return -1;
            }
            *tp++ = (uint8_t)(val >> 8) & 0xff;
            *tp++ = (uint8_t)val & 0xff;
        }
        if (colon != 0) 
        {
            /*
             * Since some memmove()'s erroneously fail to handle
             * overlapping regions, we'll do the shift by hand.
             */
            const int n = tp - colon;
            int i;

            if (tp == end)
            {
                return -1;
            }
            for (i = 1; i <= n; i++) 
            {
                end[- i] = colon[n - i];
                colon[n - i] = 0;
            }
            tp = end;
        }
        if (tp != end)
        {
            return -1;
        }
        memcpy(dst, tmp, sizeof(tmp));
        return 0;
    }

    __inline static
    size_t ip_address_btop4(const uint8_t *octs, char *dst)
    {
        static const char digits[] = "0123456789";

        size_t off = 1;  /* 1 bytes at least */
        int ch = *octs;
        int n = ch / 100;
        if (n)
        {
            ++off;  /* offset 1 */
            *dst++ = digits[n];
            ch = ch % 100;
        }
        n = ch / 10;
        if (n)
        {
            ++off;  /* offset 1 */
            *dst++ = digits[n];
            ch = ch % 10;
        }
        *dst = digits[ch];

        return off;
    }

    /**
     *  \brief          Convert an IPv4 network format address to string format
     *  \param src      The data of source
     *  \param dst      The buffer for receiving result
     *  \param size     The size of buffer
     *  \return         0 indicates success, -1 indicates failure.
     */
    __inline static
    size_t ip_address_ntop4(const void *src, char *dst, size_t size)
    {
        char tmp[sizeof("255.255.255.255")] = { 0 };
        const uint8_t *pch = (const uint8_t*)src;
        size_t len = 0;

        len += ip_address_btop4(pch++, tmp);
        tmp[len++] = '.';
        len += ip_address_btop4(pch++, tmp+len);
        tmp[len++] = '.';
        len += ip_address_btop4(pch++, tmp+len);
        tmp[len++] = '.';
        len += ip_address_btop4(pch, tmp+len);

        if (len >= size)
        {
            return 0;
        }
        memcpy(dst, tmp, len+1);

        /* length of string */
        return len;
    }

    __inline static
    size_t ip_address_btop6(const uint8_t *octs, char *dst)
    {
        static const char xdigits[] = "0123456789abcdef";

        size_t off = 1;         /* 1 bytes at least */
        int n, ch;

        ch = *octs++;
        if ((n = (ch >> 4) & 0xf) != 0)
        {
            ++off;  /* offset 1 */
            *dst++ = xdigits[n];
        }
        if ((n = ch & 0xf) != 0)
        {
            ++off;  /* offset 1 */
            *dst++ = xdigits[n];
        }

        ch = *octs;
        if ((n = (ch >> 4) & 0xf) != 0)
        {
            ++off;  /* offset 1 */
            *dst++ = xdigits[n];
        }
        *dst++ = xdigits[ch & 0xf];

        return off;
    }

    /**
     *  \brief          Convert an IPv6 network format address to string format
     *  \param src      The data of source
     *  \param dst      The buffer for receiving result
     *  \param size     The size of buffer
     *  \return         0 indicates success, -1 indicates failure.
     */
    __inline static
    size_t ip_address_ntop6(const void *src, char *dst, size_t size)
    {
        char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")] = { 0 };
        const uint16_t *pch = (const uint16_t*)src;
        const uint16_t *end = pch + 8;
        int deflat = 0;
        size_t len = 0;

        for ( ; pch < end; ++pch)
        {
            if (deflat == 0)
            {
                if (*pch)
                {
                    if (len)
                    {
                        tmp[len++] = ':';
                    }
                    len += ip_address_btop6((uint8_t*)pch, tmp+len);
                }
                else
                {
                    tmp[len++] = ':';
                    deflat = 1;   /* switch state */
                }
            }
            else if (deflat == 1)
            {
                if (*pch)
                {
                    tmp[len++] = ':';
                    len += ip_address_btop6((uint8_t*)pch, tmp+len);
                    deflat = -1;   /* switch state */
                }
            }
            else /*if (deflat == -1)*/
            {
                tmp[len++] = ':';
                len += ip_address_btop6((uint8_t*)pch, tmp+len);
            }
        }

        /* format x::? */
        if (deflat == 1)
        {
            tmp[len++] = ':';
        }

        if (len >= size)
        {
            return 0;
        }
        memcpy(dst, tmp, len+1);

        /* length of string */
        return len;
    }

    /**
     *  \brief          Set an ip address object as zero
     *  \param addr     The pointer of ip address
     */
    __inline static
    void ip_address_clear(ip_address_t *addr)
    {
        uint32_t *it = (uint32_t*)addr;

        /* sizeof(ip_address_t) eq. 20 bytes */
        *it++ = 0;  /* 01-04 bytes */
        *it++ = 0;  /* 05-08 bytes */
        *it++ = 0;  /* 09-12 bytes */
        *it++ = 0;  /* 13-16 bytes */
        *it   = 0;  /* 17-20 bytes */
    }

    /**
     *  \brief          Retrieve non-zero if they are equal
     *  \param a        The first ip address
     *  \param b        The second ip address
     *  \return         1 indicates true, 0 indicates false.
     */
    __inline static
    int ip_address_is_equal(const ip_address_t *a, const ip_address_t *b)
    {
        if (a->family != b->family)
        {
            return 0;
        }

        if (a->family == AF_INET)
        {
            if (a->ip != b->ip)
            {
                return 0;
            }
        }
        else
        {
            const uint32_t *s1 = a->all;
            const uint32_t *s2 = b->all;

            if (*s1++ != *s2++) return 0;   /* 0 */
            if (*s1++ != *s2++) return 0;   /* 1 */
            if (*s1++ != *s2++) return 0;   /* 2 */
            if (*s1   != *s2  ) return 0;   /* 3 */
        }
        /* a equal b */
        return 1;
    }

    /**
     *  \brief          Compare two ip addresses
     *  \param a        The first ip address
     *  \param b        The second ip address
     *  \return         0 indicates equation
     */
    __inline static
    int ip_address_compare(const ip_address_t *a, const ip_address_t *b)
    {
        if (a->family != b->family)
        {
            return (a->family - b->family);
        }

        if (a->family == AF_INET)
        {
            return memcmp(&a->ip, &b->ip, sizeof(b->ip));
        }

        /* others */
        return memcmp(a->all, b->all, sizeof(b->all));
    }

    /**
     *  \brief          Clone an ip address
     *  \param dst      The pointer of destination
     *  \param src      The pointer of source
     */
    __inline static
    void ip_address_clone(ip_address_t *dst, const ip_address_t *src)
    {
        uint32_t *d = (uint32_t*)dst;
        uint32_t *s = (uint32_t*)src;

        /* sizeof(ip_address_t) eq. 20 bytes */
        *d++ = *s++;  /* 01-04 bytes */
        *d++ = *s++;  /* 05-08 bytes */
        *d++ = *s++;  /* 09-12 bytes */
        *d++ = *s++;  /* 13-16 bytes */
        *d   = *s;    /* 17-20 bytes */
    }

    /**
     *  \brief          Swap two ip address
     *  \param a        The first ip address
     *  \param b        The second ip address
     */
    __inline static
    void ip_address_swap(ip_address_t *a, ip_address_t *b)
    {
        ip_address_t temp;

        ip_address_clone(&temp, a);
        ip_address_clone(a, b);
        ip_address_clone(b, &temp);
    }

    /**
     *  \brief          Parse an ip address string
     *  \param addr     The pointer of ip address
     *  \param buf      The pointer of string
     *  \return         0 indicates success, -1 indicates failure.
     */
    __inline static
    int ip_address_parse(ip_address_t *addr, const char *str)
    {
        const char *dot = strchr(str, '.');
        const char *col = strchr(str, ':');

        /* save parameter */
        uint16_t param = addr->param;

        /* initialize object */
        ip_address_clear(addr);

        /* restore parameter */
        addr->param = param;

        if (dot != NULL && col == NULL)
        {
            addr->family = AF_INET;
            if (ip_address_pton4(str, &addr->in) == 0)
            {
                return 0;
            }
        }
        else if (col != NULL)
        {
            /* note: x:x:x:x:x:x:d.d.d.d */
            addr->family = AF_INET6;
            if (ip_address_pton6(str, &addr->in6) == 0)
            {
                return 0;
            }
        }
        /* error occur */
        return -1;
    }

    /**
     *  \brief          Format an ip address as string
     *  \param addr     The pointer of ip address
     *  \param buf      The pointer of buffer
     *  \param size     The size of buffer
     *  \return         0 indicates success, -1 indicates failure.
     */
    __inline static
    int ip_address_format(const ip_address_t *addr, char *buf, size_t size)
    {
        if (addr->family == AF_INET6)
        {
            if (!ip_address_ntop6(&addr->in6, buf, size))
            {
                return -1;
            }
        }
        else if (addr->family == AF_INET)
        {
            if (!ip_address_ntop4(&addr->in, buf, size))
            {
                return -1;
            }
        }
        else
        {
            /* unknown family */
            return -1;
        }
        /* success */
        return 0;
    }

    /**
     *  \brief          Retrieve hash(32 bits) of ip address
     *  \param addr     The pointer of ip address
     *  \return         The value of hash (32 bits)
     */
    __inline static
    uint32_t ip_address_hash32(const ip_address_t *addr)
    {
        if (addr->family == AF_INET)
        {
            return ntohl(addr->ip);
        }
        return (
            ntohl(addr->all[0]) + ntohl(addr->all[1]) + 
            ntohl(addr->all[2]) + ntohl(addr->all[3])
            );
    }

    /**
     *  \brief          Retrieve hash(64 bits) of ip address
     *  \param addr     The pointer of ip address
     *  \return         The value of hash (64 bits)
     */
    __inline static
    uint64_t ip_address_hash64(const ip_address_t *addr)
    {
        if (addr->family == AF_INET)
        {
            return ntohl(addr->ip);
        }
        return (
            (((uint64_t)ntohl(addr->all[0]) << 32) | ntohl(addr->all[1])) + 
            (((uint64_t)ntohl(addr->all[2]) << 32) | ntohl(addr->all[3]))
            );
    }

    /**
     *  \brief          Retrieve an ip address as string (Non-Thread-safe)
     *  \param addr     The pointer of ip address
     *  \return         The pointer of string (non-null)
     */
    __inline static
    const char *ip_address_string(const ip_address_t *addr)
    {
        /* sizeof("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") */
        static char buf[40];

        if (ip_address_format(addr, buf, sizeof(buf)) != 0)
        {
            buf[0] = '\0';
        }
        return buf;
    }

    /**
     *  \brief          Retrieve an ip address as string (Non-Thread-safe)
     *  \param addr     The pointer of ip address
     *  \return         The pointer of string (non-null)
     */
    __inline static
    const char *ip_address_string2(const ip_address_t *addr)
    {
        /* sizeof("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") */
        static char buf[40];

        if (ip_address_format(addr, buf, sizeof(buf)) != 0)
        {
            buf[0] = '\0';
        }
        return buf;
    }


#if defined(__cplusplus)
} /* extern "C" */
#endif

/* C++ Interface of ip_address */
#if defined(__cplusplus)

/*! AD namespace
 */
namespace AD
{

    /*! IP address
     */
    struct ip_address : ip_address_t
    {
        /*! constructor
         */
        ip_address(void)
        {
            ip_address_clear(this);
        }

        /*! constructor
         */
        ip_address(const ip_address_t &addr)
        {
            ip_address_clone(this, &addr);
        }

        /*! constructor
         */
        ip_address(const char *addr)
        {
            ip_address_clear(this);
            ip_address_parse(this, addr);
        }

        /*! equal-to?
         */
        bool operator==(const ip_address &rhs) const
        {
            return ip_address_is_equal(this, &rhs);
        }

        /*! not equal-to?
         */
        bool operator!=(const ip_address &rhs) const
        {
            return (!ip_address_is_equal(this, &rhs));
        }

        /*! less-than?
         */
        bool operator<(const ip_address &rhs) const
        {
            return (ip_address_compare(this, &rhs) < 0);
        }

        /*! less-than or equal-to?
         */
        bool operator<=(const ip_address &rhs) const
        {
            return (ip_address_compare(this, &rhs) <= 0);
        }

        /*! greater-than?
         */
        bool operator>(const ip_address &rhs) const
        {
            return (ip_address_compare(this, &rhs) > 0);
        }

        /*! greater-than or equal-to?
         */
        bool operator>=(const ip_address &rhs) const
        {
            return (ip_address_compare(this, &rhs) >= 0);
        }

        /*! Retrieve in_addr of an ipv4 address
         */
        operator const in_addr&(void) const
        {
            return this->get_in();
        }

        /*! Retrieve in_addr of an ipv4 address
         */
        operator in_addr&(void)
        {
            return this->get_in();
        }

        /*! Retrieve in6_addr of an ipv6 address
         */
        operator const in6_addr&(void) const
        {
            return this->get_in6();
        }

        /*! Retrieve in6_addr of an ipv6 address
         */
        operator in6_addr&(void)
        {
            return this->get_in6();
        }

        /*! Retrieve family of ip address (0, AF_INET, AF_INET6)
         */
        uint16_t family(void) const
        {
            return ip_address_family(this);
        }

        /*! Retrieve true if it is an ipv4 address
         */
        bool is_ipv4(void) const
        {
            return ip_address_is_ipv4(this);
        }

        /*! Retrieve true if it is an ipv6 address
         */
        bool is_ipv6(void) const
        {
            return ip_address_is_ipv6(this);
        }

        /*! Retrieve true if it is a valid address
         */
        bool is_valid(void) const
        {
            return ip_address_is_valid(this);
        }

        /*! Retrieve hash(32 bits) of ip address
         */
        uint32_t hash32(void) const
        {
            return ip_address_hash32(this);
        }

        /*! Retrieve hash(64 bits) of ip address
         */
        uint64_t hash64(void) const
        {
            return ip_address_hash64(this);
        }

        /*! Format an ip address as string
         */
        bool format(char *buf, size_t size) const
        {
            return (ip_address_format(this, buf, size) == 0);
        }

        /*! Parse an ip address string
         */
        bool parse(const char *str)
        {
            return (ip_address_parse(this, str) == 0);
        }

        /*! Retrieve parameter of ip address
         */
        uint16_t get_param(void) const
        {
            return ip_address_get_param(this);
        }

        /*! Set parameter of ip address
         */
        void set_param(uint16_t val)
        {
            ip_address_set_param(this, val);
        }

        /*! Set an ipv4 address
         */
        void set(uint32_t val)
        {
            ip_address_set_ip(this, val);
        }

        /*! Set an ipv6 address
         */
        void set(const uint32_t val[4])
        {
            ip_address_set_ip6(this, val);
        }

        /*! Set an ipv4 address
         */
        void set(const in_addr &val)
        {
            ip_address_set_in(this, &val);
        }

        /*! Set an ipv6 address
         */
        void set(const in6_addr &val)
        {
            ip_address_set_in6(this, &val);
        }

        /*! Retrieve ip of an ipv4 address
         */
        uint32_t get_ip(void) const
        {
            return ip_address_get_ip(this);
        }

        /*! Retrieve ip of an ipv6 address
         */
        const uint32_t *get_ip6(void) const
        {
            return ip_address_get_ip6(this);
        }

        /*! Retrieve ip of an ipv6 address
         */
        uint32_t *get_ip6(void)
        {
            return const_cast<uint32_t*>(ip_address_get_ip6(this));
        }

        /*! Retrieve in_addr of an ipv4 address
         */
        const in_addr &get_in(void) const
        {
            return *(ip_address_get_in(this));
        }

        /*! Retrieve in_addr of an ipv4 address
         */
        in_addr &get_in(void)
        {
            return const_cast<in_addr&>(*(ip_address_get_in(this)));
        }

        /*! Retrieve in6_addr of an ipv6 address
         */
        const in6_addr &get_in6(void) const
        {
            return *(ip_address_get_in6(this));
        }

        /*! Retrieve in6_addr of an ipv6 address
         */
        in6_addr &get_in6(void)
        {
            return const_cast<in6_addr&>(*(ip_address_get_in6(this)));
        }

        /*! Clear an ip address object as zero
         */
        void clear(void)
        {
            ip_address_clear(this);
        }

        /*! Swap two ip address object
         */
        void swap(ip_address &rhs)
        {
            ip_address_swap(this, &rhs);
        }
    };
}

#endif /*__cplusplus*/

#endif /*__IP_ADDRESS_H__*/
